﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace Script.Game
{
    /// <summary>
    /// 检查是否是边界的委托
    /// </summary>
    public delegate bool CheckBoundary(Vector2Int v);
        
    /// <summary>
    /// 根据权值地图和满足权值的地图进行二次筛选的方法
    /// </summary>
    public delegate void FiltrateRange(int[] s,List<int> mapList,ref List<int> outMapList);
    /// <summary>
    /// 检查边界是否合法的委托
    /// </summary>
    public delegate bool CheckLegal(Vector2Int v);
    
    /// <summary>
    /// 获取范围的数据信息
    /// </summary>
    public struct CheckRangeInfo
    {
        /// <summary>
        /// 地图信息
        /// </summary>
        public MapInfo MapInfo;
        /// <summary>
        /// 起点位置
        /// </summary>
        public Vector2Int Pos;
        
        /// <summary>
        /// 最大权值
        /// </summary>
        public int MaxWeight;
        
        /// <summary>
        /// 范围二次处理方法
        /// 为null时使用默认方法返回全部范围
        /// </summary>
        public FiltrateRange FiltrateRangeFunc;
        
        /// <summary>
        /// 边界检查方法 获取一个位置周围的位置时使用
        /// 为null时使用默认方法返回全部范围
        /// </summary>
        public CheckBoundary CheckBoundary;

        public CheckRangeInfo(MapInfo mapInfo,Vector2Int pos,int maxWeight,FiltrateRange filtrateRangeFunc,CheckBoundary checkBoundary)
        {
            MapInfo = mapInfo;
            Pos = pos;
            MaxWeight = maxWeight;
            FiltrateRangeFunc = filtrateRangeFunc;
            CheckBoundary = checkBoundary;
        }
    }
    public static class AStart
    {
        //返回一个位置周围的位置
        /// <summary>
        /// 返回一个位置周围（符合规则）的位置
        /// </summary>
        /// <param name="pos">给定位置</param>
        /// <param name="checkBoundary">边界检查方法</param>
        /// <param name="ruleList">规则列表</param>
        /// <returns></returns>
        public static List<Vector2Int> GetAround(Vector2Int pos,CheckBoundary checkBoundary,Vector2Int[] ruleList = null )
        {
            List<Vector2Int> around = new List<Vector2Int>();
            if (ruleList == null)
            {
                ruleList = new Vector2Int[] {Vector2Int.up,Vector2Int.down,Vector2Int.left,Vector2Int.right};
            }

            foreach (var rule in ruleList)
            {
                var temp = pos + rule;
                if (checkBoundary(temp))
                {
                    around.Add(temp); 
                }
            }
            return around;
        }
        
        /// <summary>
        /// 返回某个点的周围指定权值的区域 区域使用自定义方法
        /// </summary>
        /// <param name="checkRangeInfo"></param>
        /// <returns></returns>
        public static List<Vector2Int> GetRangeListByWeightCustom(CheckRangeInfo checkRangeInfo)
        {
            MapInfo mapInfo = checkRangeInfo.MapInfo;
            Vector2Int pos= checkRangeInfo.Pos;
            int Weight = checkRangeInfo.MaxWeight;
            
            var MapData = mapInfo.Grid;
            List<Vector2Int> posList = new List<Vector2Int>();
            var MaxX = mapInfo.MapWidth();
            var MaxY = mapInfo.MapHeight();
            
            
            var start = Pos2Index(pos,MaxX); 
            //记录整张地图的权值
            int[] s = new int[MaxX*MaxY];
            //记录哪些节点是已经找过的 值为findMapList中的索引
            bool[] mapFindFlag = new bool[MaxX*MaxY];
            //记录每个节点的最短路径上一节点坐标
            int[] pre = new int[MaxX*MaxY];
            //记录哪些节点是要找的
            List<int> findMapList = new List<int>();
            //记录哪些节点是满足权值需求的
            List<int> findMapListByWeight = new List<int>();
            //初始化
            for (int j = 0; j < MaxX*MaxY; j++)
            {
                s[j] = 10000;
                mapFindFlag[j] = false;
                pre[j] = start;
            }
            //设置起点
            s[start] = 0;
            findMapList.Add(start);
            int stepsNum = 0;
            while (stepsNum < MaxX*MaxY)
            {
                var index = 0;
                var sIndex = findMapList[index];
                var over = false;
                //获取一个拥有最小权值的且没有记录过的节点
                while (mapFindFlag[sIndex])
                {
                    index++;
                    // Debug.Log(index);
                    if (findMapList.Count > index)
                    {
                        sIndex = findMapList[index];
                    }
                    else
                    {
                        over = true;
                        break;
                    }
                }

                //如果最低消耗的移动力超过上限就返回
                if (s[sIndex] > Weight || over)
                {
                    // Debug.Log($"{index},{findMapList.Count}");
                    if (index < findMapList.Count)
                    {
                        findMapList.RemoveRange(index,findMapList.Count-index);
                    }
                    if (checkRangeInfo.FiltrateRangeFunc != null)
                    {
                        checkRangeInfo.FiltrateRangeFunc(s,findMapList,ref findMapListByWeight); 
                    }
                    else
                    {
                        findMapListByWeight = findMapList;
                    }
                    return IndexList2PosList(findMapListByWeight,MaxX);
                }

                CheckBoundary checkBoundary = checkRangeInfo.CheckBoundary??delegate(Vector2Int i)
                {
                    return mapInfo.CheckGridInPosition(i);
                };
                //获取临近节点列表
                var iAround = PosList2IndexList(GetAround(Index2Pos(sIndex,MaxX), checkBoundary),MaxX);
                foreach (var v in iAround)
                {
                    //更新临近节点的权值,同时变更最短路径上一节点为当前节点
                    //新发现的边界加入到边缘集合中 (已经查找过的当然不算边缘)
                    var cost = mapInfo.GetMapCost(Index2Pos(v, MaxX));
                    if (!mapFindFlag[v] && (s[v] > (cost + s[sIndex])))
                    {
                        pre[v] = sIndex;
                        s[v] = cost + s[sIndex];
                        findMapList.Add(v);
                    }
                } 
                //标记为记录
                mapFindFlag[sIndex] = true;
               
                //手动排序实现优先队列，保证每次都是从最短路径出发
                findMapList.Sort(delegate(int x, int y)
                {
                    if (s[x] > s[y])
                    {
                        return 1;
                    }else if (s[x] == s[y])
                    {
                        return 0;
                    }

                    return -1;
                });
                stepsNum++;
            }
            return IndexList2PosList(findMapList,MaxX); 
        }
        
        /// <summary>
        /// 返回某个点的周围所有小于等于权值的区域
        /// </summary>
        /// <returns></returns>
        public static List<Vector2Int> GetRangeListAll(MapInfo mapInfo,Vector2Int pos,int movement)
        {
            var MapData = mapInfo.Grid;
            List<Vector2Int> posList = new List<Vector2Int>();
            var MaxX = mapInfo.MapWidth();
            var MaxY = mapInfo.MapHeight();
            
            
            var start = Pos2Index(pos,MaxX); 
            //记录整张地图的权值
            int[] s = new int[MaxX*MaxY];
            //记录哪些节点是已经找过的 值为findMapList中的索引
            bool[] mapFindFlag = new bool[MaxX*MaxY];
            //记录每个节点的最短路径上一节点坐标
            int[] pre = new int[MaxX*MaxY];
            //记录哪些节点是要找的
            List<int> findMapList = new List<int>();
            //初始化
            for (int j = 0; j < MaxX*MaxY; j++)
            {
                s[j] = 10000;
                mapFindFlag[j] = false;
                pre[j] = start;
            }
            //设置起点
            s[start] = 0;
            findMapList.Add(start);
            int stepsNum = 0;
            while (stepsNum < MaxX*MaxY)
            {
                var index = 0;
                var sIndex = findMapList[index];
                //获取一个拥有最小权值的且没有记录过的节点
                while (mapFindFlag[sIndex])
                {
                    index++;
                    sIndex = findMapList[index];
                }

                //如果最低消耗的移动力超过上限就返回
                if (s[sIndex] > movement)
                {
                    Debug.Log($"{index},{findMapList.Count}");
                    findMapList.RemoveRange(index,findMapList.Count-index);
                    return IndexList2PosList(findMapList,MaxX);
                }
                
                //获取临近节点列表
                var iAround = PosList2IndexList(GetAround(Index2Pos(sIndex,MaxX), delegate(Vector2Int i)
                {
                    return mapInfo.CheckGridInPosition(i);
                }),MaxX);
                foreach (var v in iAround)
                {
                    //更新临近节点的权值,同时变更最短路径上一节点为当前节点
                    //新发现的边界加入到边缘集合中 (已经查找过的当然不算边缘)
                    var cost = mapInfo.GetMapCost(Index2Pos(v, MaxX));
                    if (!mapFindFlag[v] && (s[v] > (cost + s[sIndex])))
                    {
                        pre[v] = sIndex;
                        s[v] = cost + s[sIndex];
                        findMapList.Add(v);
                    }
                } 
                //标记为记录
                mapFindFlag[sIndex] = true;
               
                //手动排序实现优先队列，保证每次都是从最短路径出发
                findMapList.Sort(delegate(int x, int y)
                {
                    if (s[x] > s[y])
                    {
                        return 1;
                    }else if (s[x] == s[y])
                    {
                        return 0;
                    }

                    return -1;
                });
                stepsNum++;
            }
            return IndexList2PosList(findMapList,MaxX); 
        }
        
        public static int Pos2Index(Vector2Int pos,int MaxX)
        {
            var y = pos.y;
            var x = pos.x;
            return y * MaxX + x;
        }
        
        public static List<int> PosList2IndexList(List<Vector2Int> posList,int MaxX)
        {
            List<int> indexList = new List<int>();
            foreach (var pos in posList)
            {
                indexList.Add(Pos2Index(pos,MaxX));
            }
            return indexList;
        }
        
        public static List<Vector2Int> IndexList2PosList(List<int> indexList,int MaxX)
        {
            List<Vector2Int> posList = new List<Vector2Int>();
            foreach (var index in indexList)
            {
                posList.Add(Index2Pos(index,MaxX));
            }
            return posList;
        }
        
        public static Vector2Int Index2Pos(int index,int MaxX)
        {
            var x = index % MaxX;
            var y = (index - x) / MaxX;
            return new Vector2Int(x,y);
        }
    }
}